package nl.dxn.tunnel.server.proxy;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.flush.FlushConsolidationHandler;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.stream.ChunkedWriteHandler;
import nl.dxn.tunnel.codec.GzipCodec;
import nl.dxn.tunnel.codec.MessageCodec;
import nl.dxn.tunnel.handel.IdleCheckHandler;
import nl.dxn.tunnel.server.handel.ProxyAgentHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

public class ProxyServer {

    private String ip;
    private int port;

    private static final Logger logger = LoggerFactory.getLogger(ProxyServer.class);

    private final ServerBootstrap bootstrap = new ServerBootstrap();
    private final NioEventLoopGroup serverBossGroup = new NioEventLoopGroup();
    private final NioEventLoopGroup serverWorkerGroup = new NioEventLoopGroup();

    public ProxyServer(String ip, int port, final HashMap<String, String> agentKeyMap) throws IOException {
        this.ip = ip;
        this.port = port;

        //SslContext sslContext = buildServerSslContext();
        bootstrap.group(serverBossGroup, serverWorkerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {

            @Override
            public void initChannel(SocketChannel ch) throws Exception {
//                ch.pipeline().addLast(sslContext.newHandler(ch.alloc()));
                ch.pipeline().addLast(new GzipCodec());
                // 固定帧长解码器
                ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                ch.pipeline().addLast(new LengthFieldPrepender(4));
                ch.pipeline().addLast(new ChunkedWriteHandler());

                // 自定义协议编解码器
                ch.pipeline().addLast(new MessageCodec());
                ch.pipeline().addLast(new IdleCheckHandler(60, 30, 0));
                // 代理客户端连接代理服务器处理器
                ch.pipeline().addLast(new FlushConsolidationHandler(1));
                ch.pipeline().addLast(new ProxyAgentHandler(agentKeyMap));
            }
        });
    }

    public void destroy() {
        try {
            serverBossGroup.shutdownGracefully();
            serverWorkerGroup.shutdownGracefully();
        } catch (Exception ex) {

        }
    }

    public boolean run() throws Exception {
        //启动服务器并绑定一个端口并且同步生成一个 ChannelFuture 对象
        ChannelFuture cf = bootstrap.bind(port).sync();
        if (cf.isSuccess()) {
            logger.info("agent服务端启动成功,本机绑定IP:{},Http端口:{}", ip, port);
            return true;
        } else {
            logger.info("服务端启动失败,本机绑定IP:{},Http端口:{},原因:{}"
                    , ip, port, cf.cause().getMessage());
            this.destroy();
            return false;
        }
    }

    private static SslContext buildServerSslContext() throws IOException {
        try (InputStream certChainFile = ClassLoader.getSystemResourceAsStream("server.crt");
             InputStream keyFile = ClassLoader.getSystemResourceAsStream("pkcs8_server.key");
             InputStream rootFile = ClassLoader.getSystemResourceAsStream("ca.crt")) {
            return SslContextBuilder.forServer(certChainFile, keyFile).trustManager(rootFile)
                    .clientAuth(ClientAuth.REQUIRE)
                    .sslProvider(SslProvider.OPENSSL).build();
        }
    }
}
